﻿#region

using System;
using System.Collections;
using System.Globalization;
using System.Reflection;
using System.Xml;
using System.Xml.Serialization;
using BTL.Technology.Framework;

#endregion

namespace BTL.Technology.Data
{
    public class DataXmlSerializer
    {
        public DataXmlSerializer(Type type)
        {
        }

        /// <summary>
        ///   Serialize an object the object schema is stored in the system cache for subsequent use
        /// </summary>
        /// <param name="writer"> </param>
        /// <param name="objObject"> </param>
        /// <param name="root"> </param>
        public void Serialize(XmlWriter writer, object objObject, string root)
        {
            if (!string.IsNullOrEmpty(root))
                writer.WriteStartElement(root);

            Serialize(writer, objObject);

            if (!string.IsNullOrEmpty(root))
                writer.WriteEndElement();
        }

        /// <summary>
        ///   Serilize an collection of object
        /// </summary>
        /// <param name="writer"> </param>
        /// <param name="objCollection"> </param>
        public void Serialize(XmlWriter writer, ICollection objCollection)
        {
            Serialize(writer, objCollection, null);
        }

        /// <summary>
        ///   Serilize an collection of object
        /// </summary>
        /// <param name="writer"> </param>
        /// <param name="objCollection"> </param>
        /// <param name="root"> </param>
        public void Serialize(XmlWriter writer, ICollection objCollection, string root)
        {
            if (!string.IsNullOrEmpty(root))
                writer.WriteStartElement(root);

            foreach (var objObject in objCollection)
            {
                Serialize(writer, objObject);
            }

            if (!string.IsNullOrEmpty(root))
                writer.WriteEndElement();
        }

        /// -----------------------------------------------------------------------------
        /// <summary>
        ///   SerializeObject serializes an Object
        /// </summary>
        /// <param name="objObject"> The object to Initialise </param>
        /// <param name="writer"> An XmlWriter to serialize to </param>
        /// -----------------------------------------------------------------------------
        public void Serialize(XmlWriter writer, object objObject)
        {
            //Try to cast the Object as IXmlSerializable 
            var xmlSerializableObject = objObject as IXmlSerializable;

            if (xmlSerializableObject == null)
            {
                Serialize(objObject, writer);
            }
            else
            {
                //Use XmlWriter
                xmlSerializableObject.WriteXml(writer);
            }
        }

        /// <summary>
        ///   Serialize an object the object schema is stored in the system cache for subsequent use
        /// </summary>
        /// <param name="writer"> </param>
        /// <param name="objObject"> </param>
        private void Serialize(object objObject, XmlWriter writer)
        {
            if (objObject.GetType().IsPrimitive 
                || objObject is string ||
                objObject is DateTime)
            {
                writer.WriteStartElement("Column");
                writer.WriteStartAttribute("Value");
                writer.WriteValue(objObject);
                writer.WriteEndAttribute();
                writer.WriteEndElement();
                return;
            }

            // get cached object mapping for type
            var objMappingInfo = ObjectMappingInfo.GetObjectMapping(objObject.GetType());

            writer.WriteStartElement(objMappingInfo.ObjectName);
            foreach (var coloumn in objMappingInfo.PropertyNames)
            {
                //If the Column matches a Property in the Object Map's PropertyInfo Dictionary
                PropertyInfo objPropertyInfo;
                if (!objMappingInfo.Properties.TryGetValue(coloumn.Key, out objPropertyInfo)) continue;

                //If property can be set
                if (!objPropertyInfo.CanRead) continue;

                //Get its type
                var propType = objPropertyInfo.PropertyType;

                //Get the Data Value from the data reader
                var coloumnValue = objPropertyInfo.GetValue(objObject, null);

                if (coloumnValue is ICollection)
                {
                    if ((coloumnValue as ICollection).Count > 0)
                        Serialize(writer, coloumnValue as ICollection, coloumn.Value);

                    continue;
                }

                if (coloumnValue is DateTime)
                {
                    var dateTime = (coloumnValue as DateTime?);

                    if (dateTime.Value.Year == 1)
                        continue;
                }
                else if (coloumnValue is Enum)
                {
                    coloumnValue = ((int) Enum.Parse(propType, coloumnValue.ToString())).ToString(CultureInfo.InvariantCulture);
                }

                if (coloumnValue == null) continue;

                writer.WriteStartAttribute(coloumn.Value);
                writer.WriteValue(coloumnValue);
                writer.WriteEndAttribute();
            }

            writer.WriteEndElement();
        }
    }
}